package ltd.sshine.mysql.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ltd.sshine.runtime.pojo.PageVO;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class DbUtils {
    /**
     * 将mybatis的page转成前端传输的PageVO
     */
    public static <T> PageVO<T> toPageVO(IPage<T> iPage) {
        PageVO<T> pageVO = new PageVO<>();
        pageVO.setCurrent(Math.toIntExact(iPage.getCurrent()));
        pageVO.setSize(Math.toIntExact(iPage.getSize()));
        pageVO.setTotal(iPage.getTotal());
        pageVO.setRecords(iPage.getRecords());
        return pageVO;
    }

    /**
     * 将mybatis的page转成前端传输的PageVO
     */
    public static <S, T> PageVO<T> toOtherPageVO(PageVO<S> pageVO, Class<T> tClass) {
        PageVO<T> target = new PageVO<>();
        target.setCurrent(pageVO.getCurrent());
        target.setSize(pageVO.getSize());
        target.setTotal(pageVO.getTotal());
        target.setRecords(pageVO.getRecords().stream().map(s -> {
            try {
                T t = tClass.getConstructor().newInstance();
                BeanUtils.copyProperties(s, t);
                return t;
            } catch (InstantiationException |
                     IllegalAccessException |
                     InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }

        }).toList());
        return target;
    }

    /**
     * 可以过滤转型
     */

    public static <T, S> PageVO<T> getPageVO(IPage<S> iPage, Class<T> tClass) {
        return getPageVO(iPage, s -> {
            try {
                T t = tClass.getConstructor().newInstance();
                BeanUtils.copyProperties(s, t);
                return t;
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                     InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 可以自定义类型转换
     */
    public static <T, S> PageVO<T> getPageVO(IPage<S> iPage, Function<S, T> function) {
        PageVO<T> pageVO = new PageVO<>();
        pageVO.setCurrent(Math.toIntExact(iPage.getCurrent()));
        pageVO.setSize(Math.toIntExact(iPage.getSize()));
        pageVO.setTotal(iPage.getTotal());
        List<S> records = iPage.getRecords();
        List<T> collect = records.stream().map(function).collect(Collectors.toList());
        pageVO.setRecords(collect);
        return pageVO;
    }

    /**
     * 可以自定义类型选择
     */
    public static <T, S> PageVO<T> getPageVO(BaseMapper<S> baseMapper, Class<T> tClass, Page<S> page) {
        QueryWrapper<S> queryWrapper = new QueryWrapper<>();
        injectSelect(tClass, queryWrapper);
        Page<S> sPage = baseMapper.selectPage(page, queryWrapper);
        return getPageVO(sPage, tClass);
    }

    /**
     * 获取选择类型，可以自定义类型选择
     */

    public static <T, S> T selectOne(BaseMapper<S> baseMapper, Class<T> tClass) {
        QueryWrapper<S> queryWrapper = new QueryWrapper<>();
        injectSelect(tClass, queryWrapper);
        S s = baseMapper.selectOne(queryWrapper);
        T t;
        try {
            t = tClass.getConstructor().newInstance();
            BeanUtils.copyProperties(s, t);
            return t;
        } catch (InstantiationException |
                 IllegalAccessException |
                 InvocationTargetException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 注入选择器
     */
    public static <T, S> void injectSelect(Class<T> tClass, QueryWrapper<S> queryWrapper) {
        Field[] declaredFields = tClass.getDeclaredFields();
        String[] objects = Arrays.stream(declaredFields)
                .map(Field::getName)
                .map(StringUtils::camelToUnderline)
                .toArray(String[]::new);
        queryWrapper.select(objects);
    }

    @SafeVarargs //禁止掉这个堆污染检查
    public static   <T> boolean isAllNull(T t, SFunction<T, ?> ...ignores){
        Set<String> ignoreSet = Arrays.stream(ignores).map(DbUtils::getFieldName).collect(Collectors.toSet());
        for (Field declaredField : t.getClass().getDeclaredFields()) {
            declaredField.setAccessible(true);
            try {
                if (ignoreSet.contains(declaredField.getName())){
                    continue;
                }
                if (Objects.nonNull(declaredField.get(t))){
                    return false;
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return true;
    }
    public static <T> String getFieldName(SFunction<T, ?> function) {
        LambdaMeta extract = LambdaUtils.extract(function);
        String implMethodName = extract.getImplMethodName();
        return PropertyNamer.methodToProperty(implMethodName);
    }

}
